package rtpsdp

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/pion/sdp/v3"
	"gogs.smarteye.com/servere/gobvrtp/track"
)

const (
	rtpmap = "rtpmap"
	bvssrc = "bvssrc"
	fmtp   = "fmtp"
)

const (
	bvFileInfo = "bvfileinfo"
)

// NewSDPFromTrack 构造sdp
func NewSDPFromTrack(tracks ...track.Track) *sdp.SessionDescription {
	b := newBuildSDP()

	return b.Build(tracks...)
}

type buildSDP struct {
}

func newBuildSDP() *buildSDP {
	return &buildSDP{}
}

func (tis *buildSDP) Build(tracks ...track.Track) *sdp.SessionDescription {
	sid, _ := track.NewSessionID()

	objectSDP := &sdp.SessionDescription{
		Version: 0,
		Origin: sdp.Origin{
			Username:       "-",
			SessionID:      sid,
			SessionVersion: uint64(time.Now().Unix()),
			NetworkType:    "IN",
			AddressType:    "IP4",
			UnicastAddress: "0.0.0.0",
		},
		SessionName:        "-",
		SessionInformation: nil,
		URI:                nil,
		TimeDescriptions: []sdp.TimeDescription{{
			Timing: sdp.Timing{
				StartTime: 0,
				StopTime:  0,
			},
		}},
		TimeZones:         nil,
		EncryptionKey:     nil,
		Attributes:        []sdp.Attribute{},
		MediaDescriptions: []*sdp.MediaDescription{},
	}

	for _, t := range tracks {
		m := tis.build(t)
		objectSDP.WithMedia(m)

		if sessionInfo := &t.MarshalFormat().SessionInfo; sessionInfo.Valid() {
			tis.updateSessionInfo(objectSDP, sessionInfo)
		}
	}

	return objectSDP
}

func (tis *buildSDP) build(t track.Track) *sdp.MediaDescription {
	mid := t.Codec().MediaType().String()
	pt := strconv.Itoa(int(t.PayloadType()))
	msId := fmt.Sprintf("smarteye bvrtc%v%v", mid, t.SSRC())

	info := t.MarshalFormat().MediaInfo
	mediaTitle := info.Information
	if len(mediaTitle) == 0 {
		mediaTitle = fmt.Sprintf("track id is %v", t.ID())
	}

	m := &sdp.MediaDescription{
		MediaName: sdp.MediaName{
			Media: mid,
			Port: sdp.RangedPort{
				Value: 0,
			},
			Protos:  info.MediaProto,
			Formats: []string{pt},
		},
		MediaTitle: func() *sdp.Information {
			v := sdp.Information(mediaTitle)
			return &v
		}(),
		ConnectionInformation: &sdp.ConnectionInformation{
			NetworkType: "IN",
			AddressType: "IP4",
			Address: &sdp.Address{
				Address: "0.0.0.0",
			},
		},
		Attributes: []sdp.Attribute{},
	}

	m.WithPropertyAttribute(sdp.AttrKeyRTCPMux)
	m.WithPropertyAttribute(sdp.AttrKeyRTCPRsize)

	switch t.Direction() {
	case track.DirectionSendOnly:
		m.WithPropertyAttribute(sdp.AttrKeySendOnly)
	case track.DirectionRecvOnly:
		m.WithPropertyAttribute(sdp.AttrKeyRecvOnly)
	case track.DirectionSendRecv:
		m.WithPropertyAttribute(sdp.AttrKeySendRecv)
	}

	m.WithValueAttribute(sdp.AttrKeyConnectionSetup, "active") // active/passive
	m.WithValueAttribute(sdp.AttrKeyMID, mid)
	m.WithValueAttribute(sdp.AttrKeyMsid, msId)
	m.WithValueAttribute(sdp.AttrKeySSRC, fmt.Sprintf("%v cname:besovideo", t.SSRC())) // ssrc: 自己的ssrc

	m.WithValueAttribute(bvssrc, fmt.Sprintf("%v", t.CtlParma().FmtContext.MediaInfo.BVSSRC)) // bvssrc: 对方的ssrc
	m.WithValueAttribute(rtpmap, fmt.Sprintf("%v %v", pt, t.RTPMap()))

	if t.Direction().HasSend() && len(t.FMTP()) > 0 {
		var parts []string
		for k, v := range t.FMTP() {
			parts = append(parts, fmt.Sprintf("%s=%s", k, v))
		}

		strFMTP := strings.Join(parts, ";")
		m.WithValueAttribute(fmtp, fmt.Sprintf("%v %v", pt, strFMTP))
	}

	return m
}

func (tis *buildSDP) updateSessionInfo(object *sdp.SessionDescription, info *track.SessionInfo) {
	if v, err := url.Parse(info.URI); err == nil {
		object.URI = v
	}

	if len(info.SessionName) > 0 {
		object.SessionName = sdp.SessionName(info.SessionName)
	}

	object.TimeDescriptions = []sdp.TimeDescription{
		{
			Timing: sdp.Timing{
				StartTime: info.StartTime,
				StopTime:  info.StopTime,
			},
		},
	}

	if len(info.Information) > 0 {
		object.SessionInformation = func() *sdp.Information {
			v := sdp.Information(info.Information)
			return &v
		}()
	}

	if len(info.JsonInfo) > 0 {
		object.WithValueAttribute(bvFileInfo, info.JsonInfo)
	}

}
